/**
 * File name: DigesterTestDriver.java Author: Willard Wang Create date: Jun 30,
 * 2013
 */
package edu.sjtu.se.prophet.driver;

import java.io.File;
import java.io.IOException;
import java.util.Iterator;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import edu.sjtu.se.prophet.exception.EdgeExistsException;
import edu.sjtu.se.prophet.exception.NodeExistsException;
import edu.sjtu.se.prophet.exception.NodeNotFoundException;
import edu.sjtu.se.prophet.graph.Graph;

/**
 * @author deathspeeder
 * 
 */
public class DigesterTestDriver {
    private static Graph readGraph(String fileName, String version)
            throws IOException, DocumentException {

        Document doc = new SAXReader().read(new File(fileName));
        Graph graph = new Graph();
        graph.getMeta().put("Version", version);
        Element root = doc.getRootElement();
        for (Iterator<Element> p = root.elementIterator("package"); p.hasNext();) {
            Element packageElement = p.next();
            if ("yes".equals(packageElement.attribute("confirmed").getText())) {
                for (Iterator<Element> c = packageElement
                        .elementIterator("class"); c.hasNext();) {
                    Element classElement = c.next();
                    if ("yes".equals(classElement.attribute("confirmed")
                            .getText())) {
                        for (Iterator<Element> f = classElement
                                .elementIterator("feature"); f.hasNext();) {
                            Element featureElement = f.next();
                            if ("yes".equals(featureElement.attribute(
                                    "confirmed").getText())) {
                                try {
                                    graph.addNode(featureElement
                                            .element("name").getText());
                                    System.out.println("Add node "
                                            + featureElement.element("name")
                                                    .getText());
                                } catch (NodeExistsException e) {
                                    // Ignore
                                }

                                for (Iterator<Element> i = featureElement
                                        .elementIterator("inbound"); i
                                        .hasNext();) {
                                    Element inElement = i.next();
                                    if ("yes".equals(inElement
                                            .attributeValue("confirmed"))
                                            && "feature".equals(inElement
                                                    .attributeValue("type"))) {
                                        try {
                                            graph.addNode(inElement.getText());
                                            System.out.println("Add node "
                                                    + inElement.getText());
                                        } catch (NodeExistsException e) {
                                            // Ignore
                                        }
                                        try {
                                            graph.addEdge(
                                                    inElement.getText(),
                                                    featureElement.element(
                                                            "name").getText());
                                            System.out.println("Add Edge "
                                                    + inElement.getText()
                                                    + ", "
                                                    + featureElement.element(
                                                            "name").getText());
                                        } catch (NodeNotFoundException e) {
                                            // Ignore
                                        } catch (EdgeExistsException e) {
                                            // Ignore
                                        }
                                    }
                                }

                                for (Iterator<Element> o = featureElement
                                        .elementIterator("outbound"); o
                                        .hasNext();) {
                                    Element outElement = o.next();
                                    if ("yes".equals(outElement
                                            .attributeValue("confirmed"))
                                            && "feature".equals(outElement
                                                    .attributeValue("type"))) {
                                        try {
                                            graph.addNode(outElement.getText());
                                            System.out.println("Add node "
                                                    + outElement.getText());
                                        } catch (NodeExistsException e) {
                                            // Ignore
                                        }
                                        try {
                                            graph.addEdge(featureElement
                                                    .element("name").getText(),
                                                    outElement.getText());
                                            System.out.println("Add Edge "
                                                    + featureElement.element(
                                                            "name").getText()
                                                    + ", "
                                                    + outElement.getText());
                                        } catch (NodeNotFoundException e) {
                                            // Ignore
                                        } catch (EdgeExistsException e) {
                                            // Ignore
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        return graph;
    }

    private static boolean fuzzyMatch(String nodeId, String method) {
        if (nodeId.equals(method))
            return true;

        nodeId = nodeId.replaceAll(" ", "");
        String[] nodeTokens = nodeId.split("[(]");
        String[] methodTokens = method.split("[(]");

        if (nodeTokens.length != methodTokens.length)
            return false;

        if (nodeTokens.length == 0)
            return true;

        if (methodTokens[0].endsWith("<init>")) {
            String[] nodePackage = nodeTokens[0].split("[.]");
            String[] methodPackage = methodTokens[0].split("[.]");
            if (nodePackage.length != methodPackage.length)
                return false;
            for (int i = 0; i < nodePackage.length - 1; i++) {
                if (!nodePackage[i].equals(methodPackage[i]))
                    return false;
            }
            if (nodePackage.length > 1
                    && nodePackage[nodePackage.length - 1]
                            .equals(nodePackage[nodePackage.length - 2]))
                return true;
            else
                return false;
        }
        if (!nodeTokens[0].equals(methodTokens[0]))
            return false;

        if (nodeTokens.length > 1) {

            String nodePara = nodeTokens[1].replaceAll("[)]", "");
            String methodPara = methodTokens[1].replaceAll("[)]", "");

            if (nodePara.equals(methodPara))
                return true;

            String[] nodeParas = nodePara.split(",");
            String[] methodParas = methodPara.split(",");

            if (nodeParas.length != methodParas.length)
                return false;

            for (int i = 0; i < nodeParas.length; i++) {
                if (!nodeParas[i].endsWith(methodParas[i]))
                    return false;
            }
        }
        return true;

    }

    /**
     * @param args
     * @throws SAXException
     * @throws IOException
     * @throws DocumentException
     */
    public static void main(String[] args) throws IOException,
            DocumentException {
        // Graph g = readGraph("test.xml", "0.1");
        // System.out.println(g.toString());
        System.out
                .println(fuzzyMatch(
                        "com.google.zxing.client.j2me.ZXingMIDlet.showError(java.lang.String)",
                        "com.google.zxing.BarcodeFormat.<init>(String)"));
        System.out.println(fuzzyMatch(
                "com.google.zxing.qrcode.detector.FinderPattern.getX()",
                "com.google.zxing.BarcodeFormat.toString()"));
        System.out.println(fuzzyMatch("com.J.J()", "com.J.<init>()"));
        System.out.println(fuzzyMatch("com.J.J", "com.J.<init>"));
        System.out.println(fuzzyMatch("com.J.J(int[])", "com.J.<init>(int[])"));
        System.out.println(fuzzyMatch("com.J.J(java.lang.String[])",
                "com.J.<init>(String[])"));
        System.out.println(fuzzyMatch("com.J.x()", "com.J.x()"));
        System.out.println(fuzzyMatch("com.J.x(com.J, com.J, int)",
                "com.J.x(J,J,int)"));
        System.out.println(fuzzyMatch("com.J.x(List<String>)",
                "com.J.x(List<String>)"));
        System.out.println(fuzzyMatch("com.J.x()", "com.J.x()"));
    }

}
